#include "sysconfig.h"
#include "AmigaConfiguration.h"
#include "sound.h"
#include "options.h"
#include "inputdevice.h"
#include "gfxfilter.h"

using namespace System;
using namespace AmiEmuLib;
using namespace AmiEmuLib::Configuration::DefaultAmigaConfigsConfig;
using namespace AmiEmuLib::Configuration::FiltersConfig;
using namespace AmiEmuLib::AmigaConfiguration;
using namespace AmiEmuLib::AmigaConfiguration::ChipsetConfig;
using namespace AmiEmuLib::AmigaConfiguration::CoreConfig;
using namespace AmiEmuLib::AmigaConfiguration::DisplayConfig;
using namespace AmiEmuLib::AmigaConfiguration::FloppiesConfig;
using namespace AmiEmuLib::AmigaConfiguration::JoyportsConfig;
using namespace AmiEmuLib::AmigaConfiguration::MemoryConfig;
using namespace AmiEmuLib::AmigaConfiguration::MountedDevicesConfig;
using namespace AmiEmuLib::AmigaConfiguration::AudioConfig;
using namespace TomanuExtensions::Utils;

#pragma warning (disable: 4564)

#undef JIT
#undef CDTV
#undef CD32
#undef AGA
#undef A2091
#undef A2065

namespace CLR
{
    namespace AmigaConfiguration
    {
        void CheckDefaults(CConfiguration* a_config)
        {
            ASSERT(a_config->AdditionalInformation == L"");
            ASSERT(a_config->config_hardware_path.IsEmpty());
            ASSERT(a_config->config_host_path == L"");

            ASSERT(a_config->gfx_mem_size == 0);

            ASSERT(a_config->sound_interpol == SOUND_INTERPOLATION_SINC);

            ASSERT(a_config->use_serial == false);
            ASSERT(a_config->serial_demand == false);
            ASSERT(a_config->serial_hwctsrts == true);
            ASSERT(a_config->serial_direct == false);
            ASSERT(a_config->parallel_matrix_emulation == 0);
            ASSERT(a_config->parallel_postscript_emulation == false);
            ASSERT(a_config->parallel_postscript_detection == false);
            ASSERT(a_config->parallel_autoflush_time == 5);
            ASSERT(a_config->ghostscript_parameters == L"");
            ASSERT(a_config->socket_emu == false);
            ASSERT(a_config->keyboard_lang == 0);

            ASSERT(a_config->jit_optcount[0] == 4);
            ASSERT(a_config->jit_optcount[1] == 0);
            ASSERT(a_config->jit_optcount[2] == 0);
            ASSERT(a_config->jit_optcount[3] == 0);
            ASSERT(a_config->jit_optcount[4] == 0);
            ASSERT(a_config->jit_optcount[5] == 0);
            ASSERT(a_config->jit_optcount[6] == -1);
            ASSERT(a_config->jit_optcount[7] == -1);
            ASSERT(a_config->jit_optcount[8] == -1);
            ASSERT(a_config->jit_optcount[9] == -1);

            ASSERT(a_config->gfx_display == 0);
            ASSERT(a_config->gfx_display_name == "");
            ASSERT(a_config->gfx_color_mode == 1);

            ASSERT(a_config->gfx_size_fs.x == 0);
            ASSERT(a_config->gfx_size_fs.y == 0);

            ASSERT(a_config->gfx_scandoubler == false);
            ASSERT(a_config->gfx_refreshrate == 0);

            ASSERT(a_config->gfx_pvsync == 0);
            ASSERT(a_config->gfx_pfullscreen == 0);

            ASSERT(a_config->gfx_resolution == 1);
            ASSERT(a_config->gfx_lores_mode == 0);

            ASSERT(a_config->gfx_blackerthanblack == false);

            ASSERT(a_config->genlock == false);
            ASSERT(a_config->chipset_refreshrate == 0);
            ASSERT(a_config->leds_on_screen == 0);
            ASSERT(a_config->keyboard_leds[0] == 0);
            ASSERT(a_config->keyboard_leds[1] == 0);
            ASSERT(a_config->keyboard_leds[2] == 0);
            ASSERT(a_config->keyboard_leds_in_use == false);
            ASSERT(a_config->scsi == 0);
            ASSERT(a_config->sana2 == 0);
            ASSERT(a_config->uaeserial == false);
            ASSERT(a_config->tod_hack == false);
            ASSERT(a_config->turbo_emulation == 0); // NO GUI

            ASSERT((a_config->cs_mbdmac == 0) || !a_config->cs_cdtvscsi); // not supported at amiemu side.

            ASSERT(a_config->a2065name == "");
            ASSERT(a_config->cs_a2091 == false);
            ASSERT(a_config->cs_a4091 == false);

            ASSERT(a_config->cpu060_revision == 1); // NO GUI
            ASSERT(a_config->fpu_revision == -1); // NO GUI

            ASSERT(a_config->picasso96_nocustom == true);
            ASSERT(a_config->picasso96_modeflags == 530);

            ASSERT(a_config->filesys_no_uaefsdb == false);
            ASSERT(a_config->filesys_custom_uaefsdb == true);
            ASSERT(a_config->uae_hide == 0);

            for (int i = 0; i < MAX_SPARE_DRIVES; i++)
                ASSERT(a_config->dfxlist[i] == "");

            ASSERT(a_config->win32_middle_mouse == true);
            ASSERT(a_config->win32_logfile == false);
            ASSERT(a_config->win32_alwaysontop == false);
            ASSERT(a_config->win32_powersavedisabled == true);
            ASSERT(a_config->win32_minimize_inactive == false);
            ASSERT(a_config->win32_statusbar == 1);
            ASSERT(a_config->win32_active_priority == 1);
            ASSERT(a_config->win32_inactive_priority == 2);
            ASSERT(a_config->win32_inactive_pause == false);
            ASSERT(a_config->win32_inactive_nosound == false);
            ASSERT(a_config->win32_iconified_priority == 3);
            ASSERT(a_config->win32_iconified_pause == true);
            ASSERT(a_config->win32_iconified_nosound == true);
            ASSERT(a_config->win32_rtgmatchdepth == true);
            ASSERT(a_config->win32_rtgscaleifsmall == true);
            ASSERT(a_config->win32_rtgallowscaling == false);
            ASSERT(a_config->win32_rtgscaleaspectratio == -1);
            ASSERT(a_config->win32_rtgvblankrate == 0);
            ASSERT(a_config->win32_borderless == false);
            ASSERT(a_config->win32_ctrl_F11_is_quit == false);
            ASSERT(a_config->win32_automount_removable == false);
            ASSERT(a_config->win32_automount_drives == false);
            ASSERT(a_config->win32_automount_cddrives == false);
            ASSERT(a_config->win32_automount_netdrives == false);
            ASSERT(a_config->win32_automount_removabledrives == false);
            ASSERT(a_config->win32_midioutdev == -2);
            ASSERT(a_config->win32_midiindev == 0);
            ASSERT(a_config->win32_uaescsimode == 0);
            ASSERT(a_config->win32_samplersoundcard == -1);
            ASSERT(a_config->win32_norecyclebin == false);
            ASSERT(a_config->win32_specialkey == 207); // NO GUI.

            ASSERT(a_config->win32_guikey == -1);
            ASSERT(a_config->win32_kbledmode == 1);

            ASSERT(a_config->statecapturerate == 250);
            ASSERT(a_config->statecapturebuffersize == 100);

            ASSERT(a_config->input_selected_setting == 3);
            ASSERT(a_config->input_joymouse_multiplier == 20);
            ASSERT(a_config->input_joymouse_deadzone == 33);
            ASSERT(a_config->input_joystick_deadzone == 33);
            ASSERT(a_config->input_joymouse_speed == 10);
            ASSERT(a_config->input_analog_joystick_mult == 15);
            ASSERT(a_config->input_analog_joystick_offset == -1);
            ASSERT(a_config->input_autofire_linecnt == 600);
            ASSERT(a_config->input_mouse_speed == 100);
            ASSERT(a_config->input_virtual_mouse == false);
            ASSERT(a_config->input_magic_mouse == false);
            ASSERT(a_config->input_magic_mouse_cursor == 0);
        }

        void SaveToXml_TestReload(CString a_filePath, CConfiguration* a_config)
        {
            try
            {
                CConfiguration config_copy = *a_config;

                CConfiguration config_reloaded;
                CLR::AmigaConfiguration::LoadFromXml(a_filePath, &config_reloaded);

                ASSERT(config_copy == *a_config);
                ASSERT(*a_config == config_reloaded);
                ASSERT(config_copy == config_reloaded);
            }
            #ifdef _MANAGED
            catch (System::Exception ^ ex)
            {
                Logger::Write(CString(ex->ToString()));
                throw;
            }
            #endif
            catch (...)
            {
                Logger::Write(L"EXCEPTION");
                throw;
            }
        }

        void LoadFromXmlCore(CConfiguration* a_config, AmigaConfig ^ a_ac)
        {
            a_config->jit_no_flags = a_ac->CPU->JIT->NoFlags;
            a_config->jit_fpu = a_ac->CPU->JIT->FPU;
            a_config->jit_fpu_more_compatible = a_ac->CPU->JIT->FPUMoreCompatible;
            a_config->jit_hard_flush = a_ac->CPU->JIT->HardFlush;
            a_config->jit_const_jump = a_ac->CPU->JIT->ConstJump;
            a_config->jit_cache_size = a_ac->CPU->JIT->CacheSize;

            if (a_ac->CPU->Speed == CPUSpeed::Adjustable)
            {
                ASSERT(a_ac->CPU->AdjustHostCPULoad == 100);
                a_config->cpu_cycle_exact = false;
                a_config->m68k_speed = -1;
                a_config->cpu_frequency = 0;
                a_config->cpu_clock_multiplier = 0;
                a_config->cpu_idle = 150;
            }
            else
            {
                a_config->m68k_speed = 0;
                a_config->cpu_cycle_exact = true;
                a_config->cpu_frequency = 0;
                a_config->cpu_clock_multiplier = (a_ac->CPU->Model == CPUModel::MC68000) ? 512 : 1024;
                a_config->cpu_idle = 0;
            }

            a_config->cpu_model = (int)a_ac->CPU->Model;

            if (a_config->cpu_model == 68021)
                a_config->cpu_model = 68020;
            if (a_config->cpu_model == 68031)
                a_config->cpu_model = 68030;
            if (a_config->cpu_model == 68041)
                a_config->cpu_model = 68040;
            if (a_config->cpu_model == 68042)
                a_config->cpu_model = 68040;
            if (a_config->cpu_model == 68061)
                a_config->cpu_model = 68060;
            if (a_config->cpu_model == 68062)
                a_config->cpu_model = 68060;

            a_config->address_space_24 = (int)a_ac->CPU->AddressSpace24Bits;
            a_config->cpu_compatible = a_ac->CPU->Prefetch;

            a_config->fpu_model = (int)a_ac->FPU->Model;

            a_config->mmu_model = (int)a_ac->MMU->Model;
        }

        static int GetAudioBufferSize(double a_latencyMS,int a_channels = 2, int a_volumeBits = 16, 
            int a_frequency = 44100)
        {
            return (int)((a_latencyMS / 1000) * a_frequency * a_channels * (a_volumeBits / 8) + 0.5);
        }

        void LoadFromXmlSound(CConfiguration* a_config, AmigaConfig ^ a_ac)
        {
            a_config->sound_produce = a_ac->Audio->Enabled;
            a_config->sound_maxbsiz = GetAudioBufferSize(a_ac->Audio->Latency);
            a_config->sound_interpol = SOUND_INTERPOLATION_SINC;
            byte trans1[] = { 0, 1, 1, 2, 2 };
            a_config->sound_filter = trans1[(int)a_ac->Audio->Filter];
            byte trans2[] = { 0, 0, 1, 0, 1 };
            a_config->sound_filter_type = trans2[(int)a_ac->Audio->Filter];
            a_config->sound_volume = 100 - a_ac->Audio->Volume;
        }

        void LoadFromXmlFloppies(CConfiguration* a_config, AmigaConfig ^ a_ac)
        {
            ASSERT(a_ac->Floppies->List[0]->Volume == a_ac->Floppies->List[1]->Volume);
            ASSERT(a_ac->Floppies->List[1]->Volume == a_ac->Floppies->List[2]->Volume);
            ASSERT(a_ac->Floppies->List[2]->Volume == a_ac->Floppies->List[3]->Volume);

            ASSERT(a_ac->Floppies->List[0]->Speed == a_ac->Floppies->List[1]->Speed);
            ASSERT(a_ac->Floppies->List[1]->Speed == a_ac->Floppies->List[2]->Speed);
            ASSERT(a_ac->Floppies->List[2]->Speed == a_ac->Floppies->List[3]->Speed);

            a_config->floppy_speed = (int)a_ac->Floppies->List[0]->Speed;
            a_config->floppy_sound_volume = a_ac->Floppies->List[0]->Volume;

            ASSERT(a_ac->Floppies->List->Count == 4);

            for (int i = 0; i < a_ac->Floppies->List->Count; i++)
            {
                FloppySlot* fs = &a_config->floppies[i];
                Floppy ^ floppy = a_ac->Floppies->List[i];

                fs->dfxtype = (int)floppy->Type;
                fs->df = CString(floppy->ImagePath);
                fs->floppy_sounds = CString(floppy->Sounds);
            }
        }

        void LoadFromXmlMemory(CConfiguration* a_config, AmigaConfig ^ a_ac)
        {
            a_config->z3fastmem_size = a_ac->Memory->Z3MemorySize;
            a_config->fastmem_size = a_ac->Memory->FastMemorySize;
            a_config->slowmem_size = a_ac->Memory->SlowMemorySize;
            a_config->chipmem_size = a_ac->Memory->ChipMemorySize;
            a_config->mbresmem_low_size = a_ac->Memory->MotherboardMemorySize;
            a_config->mbresmem_high_size = a_ac->Memory->ProcessorSlotMemorySize;
            a_config->romfile = CString(a_ac->Memory->Kickstart->FilePath);
            a_config->romextfile = CString(a_ac->Memory->KickstartExtended->FilePath);
            a_config->flashfile = CString(a_ac->Memory->FlashPath);
        }

        void LoadFromXmlDisplay(CConfiguration* a_config, AmigaConfig ^ a_ac)
        {
            a_config->gfx_framerate = a_ac->Display->RenderEveryNFrame;
            a_config->gfx_backbuffers = (int)a_ac->Display->Buffering;

            a_config->gfx_size_fs.width = a_ac->Display->Fullscreen->Resolution.Width;
            a_config->gfx_size_fs.height = a_ac->Display->Fullscreen->Resolution.Height;

            a_config->gfx_size_win.x = a_ac->Display->Window->WindowRect.Left;
            a_config->gfx_size_win.y = a_ac->Display->Window->WindowRect.Top;
            a_config->gfx_size_win.width = a_ac->Display->Window->WindowRect.Width;
            a_config->gfx_size_win.height = a_ac->Display->Window->WindowRect.Height;

            a_config->gfx_xcenter = a_ac->Display->SmartCentering ? 2 : 0;
            a_config->gfx_ycenter = a_ac->Display->SmartCentering ? 2 : 0;

            if (a_ac->Display->FullscreenActive == false)
            {
                a_config->gfx_afullscreen = 0;
                a_config->gfx_avsync = 0;
            }
            else
            {
                switch (a_ac->Display->Fullscreen->VSync)
                {
                    case VSyncMode::None:

                        a_config->gfx_afullscreen = 1;
                        a_config->gfx_avsync = 0;
                        break;

                    case VSyncMode::VSync:

                        a_config->gfx_afullscreen = 1;
                        a_config->gfx_avsync = 1;
                        break;

                    case VSyncMode::AutoVSync:

                        a_config->gfx_afullscreen = 1;
                        a_config->gfx_avsync = 2;
                        break;
                }
            }

            a_config->gfx_contrast = a_ac->Display->Contrast * 1000;
            a_config->gfx_gamma = 1000 - a_ac->Display->Gamma * 1000;
            a_config->gfx_luminance = a_ac->Display->Brightness * 1000;

            a_config->gfx_scanlines = 0;
            a_config->gfx_vresolution = 1;

            ASSERT(a_ac->Display->Fullscreen->IgnoreBorders == a_ac->Display->Window->IgnoreBorders);
            if (a_ac->Display->Window->Scaling != WindowScalingMode::ResizeWindowToAmigaDisplay)
                ASSERT(a_ac->Display->Fullscreen->ScaleFactor == a_ac->Display->Window->ScaleFactor);

            if (a_ac->Display->Fullscreen->Scaling == FullscreenScalingMode::Scale)
                ASSERT(a_ac->Display->Window->Scaling == WindowScalingMode::Scale);
            else if (a_ac->Display->Window->Scaling == WindowScalingMode::ResizeWindowToAmigaDisplay)
                ASSERT(a_ac->Display->Fullscreen->Scaling == FullscreenScalingMode::ResizeAmigaDisplayToScreen);
            else if (a_ac->Display->Window->Scaling == WindowScalingMode::ResizeAmigaDisplayToWindow)
                ASSERT(a_ac->Display->Fullscreen->Scaling == FullscreenScalingMode::ResizeAmigaDisplayToScreen);

            if (a_ac->Display->Fullscreen->Scaling == FullscreenScalingMode::ResizeAmigaDisplayToScreen)
            {
                if (a_ac->Display->Fullscreen->IgnoreBorders)
                    a_config->gfx_filter_autoscale = AUTOSCALE_NORMAL;
                else
                    a_config->gfx_filter_autoscale = AUTOSCALE_NONE;

                ASSERT(a_ac->Display->Fullscreen->ScaleFactor == ScaleFactor::Unused);
                a_config->gfx_filter_zoom_mult = 0;
            }
            else if (a_ac->Display->Fullscreen->Scaling == FullscreenScalingMode::Scale)
            {
                if (a_ac->Display->Fullscreen->ScaleFactor == ScaleFactor::Scale1x)
                    a_config->gfx_filter_zoom_mult = 1000;
                else if (a_ac->Display->Fullscreen->ScaleFactor == ScaleFactor::Scale2x)
                    a_config->gfx_filter_zoom_mult = 500;
                else if (a_ac->Display->Fullscreen->ScaleFactor == ScaleFactor::Scale3x)
                    a_config->gfx_filter_zoom_mult = 500;
                else
                    ASSERT(FALSE);

                a_config->gfx_filter_keep_aspect = 0;
                a_config->gfx_filter_autoscale = AUTOSCALE_NONE;
                a_config->gfx_filter_aspect = 0;
            }

            if (a_ac->Display->Window->Scaling == WindowScalingMode::ResizeAmigaDisplayToWindow)
            {
                if (a_ac->Display->Window->IgnoreBorders)
                    a_config->gfx_filter_autoscale = AUTOSCALE_NORMAL;
                else
                    a_config->gfx_filter_autoscale = AUTOSCALE_NONE;

                ASSERT(a_ac->Display->Fullscreen->ScaleFactor == ScaleFactor::Unused);
                a_config->gfx_filter_zoom_mult = 0;
            }
            else if (a_ac->Display->Window->Scaling == WindowScalingMode::Scale)
            {
                if (a_ac->Display->Window->ScaleFactor == ScaleFactor::Scale1x)
                    a_config->gfx_filter_zoom_mult = 1000;
                else if (a_ac->Display->Window->ScaleFactor == ScaleFactor::Scale2x)
                    a_config->gfx_filter_zoom_mult = 500;
                else if (a_ac->Display->Window->ScaleFactor == ScaleFactor::Scale3x)
                    a_config->gfx_filter_zoom_mult = 500;
                else
                    ASSERT(FALSE);

                a_config->gfx_filter_keep_aspect = 0;
                a_config->gfx_filter_autoscale = AUTOSCALE_NONE;
                a_config->gfx_filter_aspect = 0;
            }
            else if (a_ac->Display->Window->Scaling == WindowScalingMode::ResizeWindowToAmigaDisplay)
            {
                if (a_ac->Display->Window->ScaleFactor == ScaleFactor::Scale1x)
                    a_config->gfx_filter_zoom_mult = 1000;
                else if (a_ac->Display->Window->ScaleFactor == ScaleFactor::Scale2x)
                    a_config->gfx_filter_zoom_mult = 500;
                else if (a_ac->Display->Window->ScaleFactor == ScaleFactor::Scale3x)
                    a_config->gfx_filter_zoom_mult = 500;
                else
                    ASSERT(FALSE);

                a_config->gfx_filter_keep_aspect = 0;
                a_config->gfx_filter_autoscale = AUTOSCALE_RESIZE;
                a_config->gfx_filter_aspect = 0;
            }

            a_config->gfx_filter_keep_aspect = true;
            a_config->gfx_filter_aspect = -1;

            Filter ^ filter = Filters::GetFilterByDisplayName(a_ac->Display->FilterName);
            if (filter != nullptr)
            {
                if (filter->Type == FilterType::Software)
                {
                    int index = 0;
                    while (uaefilters[index].name != nullptr)
                    {
                        if (uaefilters[index].name == CString(filter->Name))
                            break;
                        index++;
                    }

                    a_config->gfx_filter = index + 1;
                    a_config->gfx_filtershader = "";
                }
                else if (filter->Type == FilterType::Shader)
                {
                    a_config->gfx_filtershader = CString(filter->Name);
                    a_config->gfx_filter = 0;
                }
            }
        }

        void LoadFromXmlChipset(CConfiguration* a_config, AmigaConfig ^ a_ac)
        {
            a_config->chipset_mask = (uint)a_ac->Chipset->Type;
            a_config->blitter_cycle_exact = (a_ac->CPU->Speed == CPUSpeed::CycleExact);
            a_config->cs_ciaatod = (int)a_ac->Chipset->CIAATODSource;
            a_config->cs_rtc = (int)a_ac->Chipset->RealTimeClock;
            a_config->cs_ciaoverlay = a_ac->Chipset->CIAOverlay;
            a_config->cs_ksmirror_e0 = a_ac->Chipset->RomMirrorE0;
            a_config->cs_ksmirror_a8 = a_ac->Chipset->RomMirrorA8;
            a_config->cs_ide = (int)a_ac->Chipset->IDE;
            a_config->cs_fatgaryrev = (int)a_ac->Chipset->Gary;
            a_config->cs_pcmcia = a_ac->Chipset->PCMCIA;
            a_config->cs_df0idhw = a_ac->Chipset->DF0IDHardware;
            a_config->cs_slowmemisfast = a_ac->Chipset->SlowMemoryIsFast;
            a_config->cs_resetwarning = a_ac->Chipset->KeyboardResetWarning;
            a_config->cs_denisenoehb = a_ac->Chipset->DeniseNoEHB;
            a_config->cs_agnusbltbusybug = a_ac->Chipset->AgnusBlitterBusyBug;
            a_config->cs_dipagnus = a_ac->Chipset->A1000Agnus;
            a_config->cs_ramseyrev = (int)a_ac->Chipset->Ramsey;
            a_config->cs_a1000ram = a_ac->Chipset->A1000BootRamRom;
            a_config->ntscmode = (a_ac->Chipset->TVMode == TVMode::NTSC);

            a_config->immediate_blits = a_ac->Chipset->BlitterImmediate;
            a_config->collision_level = (int)a_ac->Chipset->SpriteCollisionLevel;

            switch (a_ac->AmigaModel)
            {
                case AmigaModel::CDTV: a_config->cs_compatible = CP_CDTV; break;
                case AmigaModel::CD32: a_config->cs_compatible = CP_CD32; break;
                case AmigaModel::A500: a_config->cs_compatible = CP_A500; break;
                case AmigaModel::A500_Plus: a_config->cs_compatible = CP_A500P; break;
                case AmigaModel::A600: a_config->cs_compatible = CP_A600; break;
                case AmigaModel::A1000: a_config->cs_compatible = CP_A1000; break;
                case AmigaModel::A1200: a_config->cs_compatible = CP_A1200; break;
                case AmigaModel::A3000: a_config->cs_compatible = CP_A3000; break;
                case AmigaModel::A4000: a_config->cs_compatible = CP_A4000; break;
                default: ASSERT(FALSE); break;
            }

            a_config->cs_mbdmac = (a_ac->Chipset->DMAC == DMACType::None) ? 0 :
                                  (a_ac->Chipset->DMAC == DMACType::CDTV) ? 0 :
                                  (a_ac->Chipset->DMAC == DMACType::A3000) ? 1 : 2;

            a_config->cs_cd32cd = a_ac->Chipset->CD32CD;
            a_config->cs_cd32c2p = a_ac->Chipset->AkikoChunkyToPlanar;
            a_config->cs_cd32nvram = a_ac->Chipset->CD32NVRAM;

            a_config->cs_cdtvcd = a_ac->Chipset->CDTVCD;
            a_config->cs_cdtvram = a_ac->Chipset->CDTVBatteryRAM;
            a_config->cs_cdtvcard = (a_ac->Chipset->CDTVMemoryCard == CDTVMemoryCardType::Card64KB) ? 64 : 0;
            ASSERT(a_ac->Chipset->CDTVMemoryCard != CDTVMemoryCardType::Card256KB);
        }

        void LoadFromXmlMountedDevices(CConfiguration* a_config, AmigaConfig ^ a_ac)
        {
            a_config->mountitems = a_ac->MountedDevices->List->Count;

            for (int i = 0; i < MOUNT_CONFIG_SIZE; i++)
                a_config->mounted_devices[i] = DeviceConfigInfo();

            for (int i = 0; i < a_config->mountitems; i++)
            {
                DeviceConfigInfo* md = &a_config->mounted_devices[i];

                if (a_ac->MountedDevices->List[i]->GetType() == Hardfile::typeid )
                {
                    Hardfile ^ hf = safe_cast<Hardfile ^>(a_ac->MountedDevices->List[i]);

                    md->file_sys = CString(hf->FileSystem);
                    md->rootdir = CString(hf->FilePath);
                    md->device_name = CString(hf->DeviceName);
                    md->surfaces = hf->Surfaces;
                    md->sectors = hf->Sectors;
                    md->reserved = hf->Reserved;
                    md->blocksize = hf->BlockSize;
                    md->controller = (int)hf->Controller;
                    md->bootpri = hf->BootPriority;
                    md->readonly = hf->ReadOnly;
                    md->autoboot = hf->Bootable;
                    md->donotmount = !hf->Mount;
                    md->ishdf = true;

                    md->configoffset = -1;
                }
                else if (a_ac->MountedDevices->List[i]->GetType() == Filesystem::typeid )
                {
                    Filesystem ^ fs = safe_cast<Filesystem ^>(a_ac->MountedDevices->List[i]);

                    md->rootdir = CString(fs->DirectoryPath);
                    md->volume_name = CString(fs->VolumeName);
                    md->device_name = CString(fs->DeviceName);
                    md->bootpri = fs->BootPriority;
                    md->readonly = fs->ReadOnly;
                    md->autoboot = fs->Bootable;
                    md->donotmount = !fs->Mount;

                    md->configoffset = -1;
                }
                else
                    ASSERT(FALSE);
            }
        }

        void LoadFromXmlJoyports(CConfiguration* a_config, AmigaConfig ^ a_ac)
        {
            a_config->jports[0].id = JSEM_MICE;
            a_config->jports[0].mode = JSEM_MODE_MOUSE;
            a_config->jports[1].id = 1;
            a_config->jports[1].mode = JSEM_MODE_JOYSTICK;
        }

        void LoadFromXml(CString a_filePath, CConfiguration* a_config)
        {
            a_config->clear();
            default_prefs(a_config);

            AmigaConfig ^ ac = gcnew AmigaConfig(gcnew String(a_filePath));

            a_config->Description = CString(ac->Description);
            a_config->AdditionalInformation = "";

            LoadFromXmlCore(a_config, ac);
            LoadFromXmlSound(a_config, ac);
            LoadFromXmlChipset(a_config, ac);
            LoadFromXmlMemory(a_config, ac);
            LoadFromXmlMountedDevices(a_config, ac);
            LoadFromXmlDisplay(a_config, ac);
            LoadFromXmlFloppies(a_config, ac);
            LoadFromXmlJoyports(a_config, ac);

            CheckDefaults(a_config);
        }

        void SaveToXmlCore(AmigaConfig ^ a_ac, CConfiguration* a_config)
        {
            a_ac->CPU->JIT->Enabled = a_config->jit_cache_size > 0;
            a_ac->CPU->JIT->NoFlags = a_config->jit_no_flags;
            a_ac->CPU->JIT->FPU = a_config->jit_fpu;
            a_ac->CPU->JIT->FPUMoreCompatible = a_config->jit_fpu_more_compatible;
            a_ac->CPU->JIT->HardFlush = a_config->jit_hard_flush;
            a_ac->CPU->JIT->ConstJump = a_config->jit_const_jump;
            a_ac->CPU->JIT->CacheSize = a_config->jit_cache_size;

            if (a_config->m68k_speed == 0)
            {
                ASSERT(a_config->cpu_cycle_exact);
                ASSERT(a_config->cpu_idle == 0);
                ASSERT(a_config->cpu_frequency == 0);

                if (a_config->cpu_model == 68000)
                    ASSERT(a_config->cpu_clock_multiplier == 512);
                else if (a_config->cpu_model == 68020)
                    ASSERT(a_config->cpu_clock_multiplier == 1024);
                else
                    ASSERT(FALSE);

                a_ac->CPU->Speed = CPUSpeed::CycleExact;
            }
            else if (a_config->m68k_speed == -1)
            {
                ASSERT(!a_config->cpu_cycle_exact);
                ASSERT(a_config->cpu_idle == 150);
                ASSERT(a_config->cpu_frequency == 0);
                ASSERT(a_config->cpu_clock_multiplier == 0);

                a_ac->CPU->Speed = CPUSpeed::Adjustable;
                a_ac->CPU->AdjustHostCPULoad = 100;
            }
            else
                ASSERT(FALSE);

            a_ac->CPU->Model = (CPUModel)a_config->cpu_model;
            if (a_config->address_space_24 && (a_config->cpu_model == 68020))
                a_ac->CPU->Model = CPUModel::MC68EC020;
            a_ac->CPU->Prefetch = a_config->cpu_compatible;

            a_ac->FPU->Model = (FPUModel)a_config->fpu_model;

            a_ac->MMU->Model = (MMUModel)a_config->mmu_model;
        }

        static double GetAudioLatencyMS(int a_bufferSize, int a_channels = 2, int a_volumeBits = 16,
            int a_frequency = 44100)
        {
            return (double)a_bufferSize / a_channels / (a_volumeBits / 8) / a_frequency * 1000;
        }

        void SaveToXmlSound(AmigaConfig ^ a_ac, CConfiguration* a_config)
        {
            a_ac->Audio->Enabled = a_config->sound_produce;
            a_ac->Audio->Latency = GetAudioLatencyMS(a_config->sound_maxbsiz, 2, 16, 44100);
            int trans1[] = { 0, 0, 1, 2, 3, 4 };
            a_ac->Audio->Filter = (AmigaAudioFilter)trans1[a_config->sound_filter * 2 + a_config->sound_filter_type];
            a_ac->Audio->Volume = 100 - a_config->sound_volume;
        }

        void SaveToXmlChipset(AmigaConfig ^ a_ac, CConfiguration* a_config)
        {
            a_ac->Chipset->Type = (ChipsetType)a_config->chipset_mask;
            a_ac->Chipset->CIAATODSource = (CIAATODSource)a_config->cs_ciaatod;
            a_ac->Chipset->RealTimeClock = (RealTimeClockType)a_config->cs_rtc;
            a_ac->Chipset->CIAOverlay = a_config->cs_ciaoverlay;
            a_ac->Chipset->RomMirrorE0 = a_config->cs_ksmirror_e0;
            a_ac->Chipset->RomMirrorA8 = a_config->cs_ksmirror_a8;
            a_ac->Chipset->IDE = (IDEType)a_config->cs_ide;
            a_ac->Chipset->Gary = (GaryType)a_config->cs_fatgaryrev;
            a_ac->Chipset->PCMCIA = a_config->cs_pcmcia;
            a_ac->Chipset->DF0IDHardware = a_config->cs_df0idhw;
            a_ac->Chipset->SlowMemoryIsFast = a_config->cs_slowmemisfast;
            a_ac->Chipset->KeyboardResetWarning = a_config->cs_resetwarning;
            a_ac->Chipset->DeniseNoEHB = a_config->cs_denisenoehb;
            a_ac->Chipset->AgnusBlitterBusyBug = a_config->cs_agnusbltbusybug;
            a_ac->Chipset->A1000Agnus = a_config->cs_dipagnus;
            a_ac->Chipset->Ramsey = (RamseyType)a_config->cs_ramseyrev;
            a_ac->Chipset->A1000BootRamRom = a_config->cs_a1000ram;
            a_ac->Chipset->TVMode = a_config->ntscmode ? TVMode::NTSC : TVMode::PAL;

            a_ac->Chipset->BlitterImmediate = a_config->immediate_blits;
            a_ac->Chipset->SpriteCollisionLevel = (SpriteCollisionLevel)a_config->collision_level;

            switch (a_config->cs_compatible)
            {
                case CP_CDTV: a_ac->AmigaModel = AmigaModel::CDTV; break;
                case CP_CD32: a_ac->AmigaModel = AmigaModel::CD32; break;
                case CP_A500: a_ac->AmigaModel = AmigaModel::A500; break;
                case CP_A500P: a_ac->AmigaModel = AmigaModel::A500_Plus; break;
                case CP_A600: a_ac->AmigaModel = AmigaModel::A600; break;
                case CP_A1000: a_ac->AmigaModel = AmigaModel::A1000; break;
                case CP_A1200: a_ac->AmigaModel = AmigaModel::A1200; break;
                case CP_A3000: a_ac->AmigaModel = AmigaModel::A3000; break;
                case CP_A4000: a_ac->AmigaModel = AmigaModel::A4000; break;
                default: ASSERT(FALSE); break;
            }

            if ((a_config->cs_compatible == CP_CDTV) || a_config->cs_cdtvcd)
                a_ac->Chipset->DMAC = DMACType::CDTV;
            if (a_config->cs_mbdmac > 0)
                a_ac->Chipset->DMAC = (a_config->cs_mbdmac == 1) ? DMACType::A3000 : DMACType::A4000;

            a_ac->Chipset->CD32CD = a_config->cs_cd32cd;
            a_ac->Chipset->AkikoChunkyToPlanar = a_config->cs_cd32c2p;
            a_ac->Chipset->CD32NVRAM = a_config->cs_cd32nvram;

            a_ac->Chipset->CDTVCD = a_config->cs_cdtvcd;
            a_ac->Chipset->CDTVBatteryRAM = a_config->cs_cdtvram;
            a_ac->Chipset->CDTVMemoryCard = (a_config->cs_cdtvcard == 64) ? CDTVMemoryCardType::Card64KB : CDTVMemoryCardType::None;
        }

        void SaveToXmlMemory(AmigaConfig ^ a_ac, CConfiguration* a_config)
        {
            a_ac->Memory->Z3MemorySize = a_config->z3fastmem_size;
            a_ac->Memory->FastMemorySize = a_config->fastmem_size;
            a_ac->Memory->SlowMemorySize = a_config->slowmem_size;
            a_ac->Memory->ChipMemorySize = a_config->chipmem_size;
            a_ac->Memory->MotherboardMemorySize = a_config->mbresmem_low_size;
            a_ac->Memory->ProcessorSlotMemorySize = a_config->mbresmem_high_size;
            a_ac->Memory->FlashPath = gcnew String(a_config->flashfile);

            a_ac->Memory->Kickstart->FilePath = gcnew String(a_config->romfile);
            if (a_config->romfile != "")
            {
                a_ac->Memory->Kickstart->SHA1 = 
                    Hash::CalculateSHA1ForFile(a_ac->Memory->Kickstart->FilePath);
            }
            a_ac->Memory->KickstartExtended->FilePath = gcnew String(a_config->romextfile);
            if (a_config->romextfile != "")
            {
                a_ac->Memory->KickstartExtended->SHA1 = 
                    Hash::CalculateSHA1ForFile(a_ac->Memory->KickstartExtended->FilePath);
            }
        }

        void SaveToXmlMountedDevices(AmigaConfig ^ a_ac, CConfiguration* a_config)
        {
            for (int i = 0; i < a_config->mountitems; i++)
            {
                DeviceConfigInfo* md = &a_config->mounted_devices[i];

                if (md->ishdf)
                {
                    if ((md->rootdir != "") && (md->rootdir[0] == ':'))
                    {
                        ASSERT(FALSE);
                    }
                    else
                    {
                        Hardfile ^ hf = gcnew Hardfile();

                        hf->FileSystem = gcnew String(md->file_sys);
                        hf->FilePath = gcnew String(md->rootdir);
                        hf->DeviceName = gcnew String(md->device_name);
                        hf->Surfaces = md->surfaces;
                        hf->Sectors = md->sectors;
                        hf->Reserved = md->reserved;
                        hf->BlockSize = md->blocksize;
                        hf->Controller = (MountedDeviceController)md->controller;
                        hf->BootPriority = md->bootpri;
                        hf->ReadOnly = md->readonly;
                        hf->Bootable = md->autoboot;
                        hf->Mount = !md->donotmount;

                        ASSERT(md->configoffset == -1);

                        a_ac->MountedDevices->List->Add(hf);
                    }
                }
                else
                {
                    Filesystem ^ fs = gcnew Filesystem();

                    fs->DirectoryPath = gcnew String(md->rootdir);
                    fs->VolumeName = gcnew String(md->volume_name);
                    fs->DeviceName = gcnew String(md->device_name);
                    fs->BootPriority = md->bootpri;
                    fs->ReadOnly = md->readonly;
                    fs->Bootable = md->autoboot;
                    fs->Mount = !md->donotmount;

                    ASSERT(md->configoffset == -1);

                    a_ac->MountedDevices->List->Add(fs);
                }
            }
        }

        void SaveToXmlDisplay(AmigaConfig ^ a_ac, CConfiguration* a_config)
        {
            a_ac->Display->RenderEveryNFrame = a_config->gfx_framerate;
            a_ac->Display->Buffering = (BufferingMode)a_config->gfx_backbuffers;

            a_ac->Display->Fullscreen->Resolution.Width = a_config->gfx_size_fs.width;
            a_ac->Display->Fullscreen->Resolution.Height = a_config->gfx_size_fs.height;

            a_ac->Display->Window->WindowRect.X = a_config->gfx_size_win.x;
            a_ac->Display->Window->WindowRect.Y = a_config->gfx_size_win.y;
            a_ac->Display->Window->WindowRect.Width = a_config->gfx_size_win.width;
            a_ac->Display->Window->WindowRect.Height = a_config->gfx_size_win.height;

            if (a_config->gfx_afullscreen == 0)
                a_ac->Display->FullscreenActive = false;
            else if (a_config->gfx_afullscreen == 1)
            {
                a_ac->Display->FullscreenActive = true;

                if (a_config->gfx_avsync == 0)
                    a_ac->Display->Fullscreen->VSync = VSyncMode::None;
                else if (a_config->gfx_avsync == 1)
                    a_ac->Display->Fullscreen->VSync = VSyncMode::VSync;
                else if (a_config->gfx_avsync == 2)
                    a_ac->Display->Fullscreen->VSync = VSyncMode::AutoVSync;
            }
            else if (a_config->gfx_afullscreen == 2)
            {
                a_ac->Display->FullscreenActive = true;
                a_ac->Display->Fullscreen->VSync = VSyncMode::None;
            }

            a_ac->Display->SmartCentering = (a_config->gfx_xcenter == 2) && (a_config->gfx_ycenter == 2);

            a_ac->Display->Contrast = a_config->gfx_contrast / 1000.0f;
            a_ac->Display->Gamma = (1000.0f - a_config->gfx_gamma) / 1000.0f;
            a_ac->Display->Brightness = a_config->gfx_luminance / 1000.0f;

            ASSERT(a_config->gfx_scanlines == 0);
            ASSERT(a_config->gfx_vresolution == 1);

            ASSERT(a_config->gfx_filter_aspect == -1);
            ASSERT(a_config->gfx_filter_keep_aspect == 1);

            if (a_config->gfx_filter_autoscale == AUTOSCALE_NONE)
            {
                if (a_config->gfx_filter_zoom_mult == 0)
                {
                    a_ac->Display->Fullscreen->Scaling = FullscreenScalingMode::ResizeAmigaDisplayToScreen;
                    a_ac->Display->Fullscreen->ScaleFactor = ScaleFactor::Unused;
                }
                else
                {
                    a_ac->Display->Fullscreen->Scaling = FullscreenScalingMode::Scale;

                    if (a_config->gfx_filter_zoom_mult == 0)
                        a_ac->Display->Fullscreen->ScaleFactor = ScaleFactor::Scale1x;
                    else if (a_config->gfx_filter_zoom_mult == 1000)
                        a_ac->Display->Fullscreen->ScaleFactor = ScaleFactor::Scale1x;
                    else if (a_config->gfx_filter_zoom_mult == 500)
                        a_ac->Display->Fullscreen->ScaleFactor = ScaleFactor::Scale2x;
                    else
                        ASSERT(FALSE);
                }

                a_ac->Display->Fullscreen->IgnoreBorders = false;
            }
            else if (a_config->gfx_filter_autoscale == AUTOSCALE_NORMAL)
            {
                ASSERT(a_config->gfx_filter_zoom_mult == 0);

                a_ac->Display->Fullscreen->Scaling = FullscreenScalingMode::ResizeAmigaDisplayToScreen;
                a_ac->Display->Fullscreen->IgnoreBorders = true;
                a_ac->Display->Fullscreen->ScaleFactor = ScaleFactor::Unused;
            }
            else if (a_config->gfx_filter_autoscale == AUTOSCALE_RESIZE)
            {
                a_ac->Display->Fullscreen->Scaling = FullscreenScalingMode::ResizeAmigaDisplayToScreen;
                a_ac->Display->Fullscreen->IgnoreBorders = false;
                a_ac->Display->Fullscreen->ScaleFactor = ScaleFactor::Unused;
            }

            if (a_config->gfx_filter_autoscale == AUTOSCALE_NONE)
            {
                if (a_config->gfx_filter_zoom_mult == 0)
                {
                    a_ac->Display->Window->Scaling = WindowScalingMode::ResizeAmigaDisplayToWindow;
                    a_ac->Display->Window->ScaleFactor = ScaleFactor::Unused;
                }
                else
                {
                    a_ac->Display->Window->Scaling = WindowScalingMode::Scale;

                    if (a_config->gfx_filter_zoom_mult == 0)
                        a_ac->Display->Window->ScaleFactor = ScaleFactor::Scale1x;
                    else if (a_config->gfx_filter_zoom_mult == 1000)
                        a_ac->Display->Window->ScaleFactor = ScaleFactor::Scale1x;
                    else if (a_config->gfx_filter_zoom_mult == 500)
                        a_ac->Display->Window->ScaleFactor = ScaleFactor::Scale2x;
                    else
                        ASSERT(FALSE);
                }

                a_ac->Display->Window->IgnoreBorders = false;
            }
            else if (a_config->gfx_filter_autoscale == AUTOSCALE_NORMAL)
            {
                ASSERT(a_config->gfx_filter_zoom_mult == 0);

                a_ac->Display->Window->Scaling = WindowScalingMode::ResizeAmigaDisplayToWindow;
                a_ac->Display->Window->IgnoreBorders = true;
            }
            else if (a_config->gfx_filter_autoscale == AUTOSCALE_RESIZE)
            {
                a_ac->Display->Window->Scaling = WindowScalingMode::ResizeWindowToAmigaDisplay;
                a_ac->Display->Window->IgnoreBorders = false;

                if (a_config->gfx_filter_zoom_mult == 1000)
                    a_ac->Display->Window->ScaleFactor = ScaleFactor::Scale1x;
                else if (a_config->gfx_filter_zoom_mult == 500)
                    a_ac->Display->Window->ScaleFactor = ScaleFactor::Scale2x;
                else
                    ASSERT(FALSE);
            }

            if (a_config->gfx_filter != 0)
            {

                a_ac->Display->FilterName = Filters::GetFilterByName(
                    gcnew String(uaefilters[a_config->gfx_filter - 1].name), FilterType::Software)->DisplayName;
            }
            else if (a_config->gfx_filtershader != "")
            {
                a_ac->Display->FilterName = Filters::GetFilterByName(
                    gcnew String(uaefilters[a_config->gfx_filter - 1].name), FilterType::Shader)->DisplayName;
            }
        }

        void SaveToXmlFloppies(AmigaConfig ^ a_ac, CConfiguration* a_config)
        {
            a_ac->Floppies->List[0]->Speed = (FloppySpeed)a_config->floppy_speed;
            a_ac->Floppies->List[1]->Speed = (FloppySpeed)a_config->floppy_speed;
            a_ac->Floppies->List[2]->Speed = (FloppySpeed)a_config->floppy_speed;
            a_ac->Floppies->List[3]->Speed = (FloppySpeed)a_config->floppy_speed;

            a_ac->Floppies->List[0]->Volume = a_config->floppy_sound_volume;
            a_ac->Floppies->List[1]->Volume = a_config->floppy_sound_volume;
            a_ac->Floppies->List[2]->Volume = a_config->floppy_sound_volume;
            a_ac->Floppies->List[3]->Volume = a_config->floppy_sound_volume;

            for (int i = 0; i < 4; i++)
            {
                FloppySlot* fs = &a_config->floppies[i];
                Floppy ^ floppy = a_ac->Floppies->List[i];

                floppy->Type = (FloppyType)fs->dfxtype;
                floppy->ImagePath = gcnew String(fs->df);
                floppy->Sounds = gcnew String(fs->floppy_sounds);
            }
        }

        void SaveToXmlJoyports(AmigaConfig ^ a_ac, CConfiguration* a_config)
        {
            ASSERT(a_config->jports[0].id == JSEM_MICE);
            ASSERT(a_config->jports[0].mode == JSEM_MODE_MOUSE);
            ASSERT(a_config->jports[0].autofire == 0);
            ASSERT(a_config->jports[1].id == 1);
            ASSERT(a_config->jports[1].mode == JSEM_MODE_JOYSTICK);
            ASSERT(a_config->jports[1].autofire == 0);
            ASSERT(a_config->jports[2].id == -1);
            ASSERT(a_config->jports[3].id == -1);
        }

        void SaveToXml(CString a_filePath, CConfiguration* a_config)
        {
            CheckDefaults(a_config);

            AmigaConfig ^ ac = DefaultAmigaConfigs::GetConfig(AmigaModel::A500);

            ac->Description = gcnew String(a_config->Description);

            SaveToXmlCore(ac, a_config);
            SaveToXmlSound(ac, a_config);
            SaveToXmlChipset(ac, a_config);
            SaveToXmlMemory(ac, a_config);
            SaveToXmlMountedDevices(ac, a_config);
            SaveToXmlDisplay(ac, a_config);
            SaveToXmlFloppies(ac, a_config);
            SaveToXmlJoyports(ac, a_config);

            ac->Save(gcnew String(a_filePath));

            SaveToXml_TestReload(a_filePath, a_config);
        }

        CString AddXmlExtension(CString a_path)
        {
            return CString(System::IO::Path::ChangeExtension(gcnew String(a_path), L"xml"));
        }
    }
}